/*********************************************************************************************************
**
**                                   GJB 标准测试集
**
**                               Copyright All Rights Reserved
**
**--------------文件信息--------------------------------------------------------------------------------
**
** 文   件   名: gjb_S0101802GN.c
**
** 文件创建日期: 2021 年 1 月 12 日
**
** 描        述: 标准套接字编程接口功能测试。利用标准套接字接口编写
**               客户和服务器端网络程序，实现网络功能。
*********************************************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <spawn.h>
#include <wait.h>
#include <sys/types.h>

#ifndef TEST_IP
#define TEST_IP "192.168.6.128"
#endif

#define IP_ADDR TEST_IP

void server_test()
{
    int s,on = 1;
    int ret1 = -2;
    int ret2 = -2;

    int newSock;
    struct sockaddr_in addr;
    int addrLen;

    int ret3 = -2;
    int ret4 = -2;
    int ret5 = -2;
    int ret6 = -2;

    char recvbuf[1024] = {0};
    int  buf_len = sizeof(recvbuf);
    char sendbuf[] = "This is a message from 192.168.1.110";
    int  buf_len1 = sizeof(sendbuf);
    struct sockaddr_in srvr;

    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == -1) {
        printf("socket error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "socket error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("socket success!\n");
        // GJB_PRT_INFO( "socket success!\n");
    }

    /*
     *  设置 socket 选项 SO_REUSEADDR
     端口复用技术（setsockopt（））
     */
    ret1 = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
    if(ret1 == -1) {
        printf("setsockopt error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "setsockopt error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("setsockopt success!\n");
        // GJB_PRT_INFO( "setsockopt success!\n");
    }

    int size = sizeof(on);
    ret6 = getsockopt( s, SOL_SOCKET, SO_REUSEADDR,
                       (char *)&on, (socklen_t *)&size);
    if(ret6 == -1) {
        printf("getsockopt error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "getsockopt error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("getsockopt success!\n");
        // GJB_PRT_INFO( "getsockopt success!\n");
    }

    memset(&srvr, 0, sizeof(srvr));
/*
struct sockaddr_in {
    short sa_family;//地址族，2字节
    unsigned short int sin_port;//端口号，2字节
    struct in_addr sin_addr;//IP地址，4字节
    unsigned char sin_zero[];//填充0以保持与struct sockaddr同样大小，8字节
}
*/
    srvr.sin_family = AF_INET;
    srvr.sin_port = htons(8081);
    srvr.sin_addr.s_addr = inet_addr(IP_ADDR);

    ret2 = bind(s, (struct sockaddr *)&srvr, sizeof(srvr));
    if (ret2 == -1) {
        printf("bind error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "bind error!\n");
        // return (GJB_PRI_FAIL());

    } else {
        printf("bind success!\n");
        // GJB_PRT_INFO( "bind success!\n");
    }

    /*
     *  监听 socket
     */
    ret3 = listen(s, 10);
    if (ret3 == -1) {
        printf("listen error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "listen error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("listen success!\n");
        // GJB_PRT_INFO( "listen success!\n");
    }

    memset(&addr, 0, sizeof(addr));
    addrLen = sizeof (struct sockaddr);

    newSock = accept(s, (struct sockaddr *)&addr, (socklen_t *)&addrLen);
    if (newSock == -1) {
        printf("accept error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "accept error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("accept success!\n");
        // GJB_PRT_INFO( "accept success!\n");
    }

    ret4 = recv(newSock, recvbuf, buf_len, 0);
    if(ret4 == -1) {
        printf("recv error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "recv error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("recv success!\n");
        // GJB_PRT_INFO( "recv success!\n");
    }

    ret5 = send(newSock, sendbuf, buf_len1, 0);
    if (ret5 == -1) {
        printf("send error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "send error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("send success!\n");
        // GJB_PRT_INFO( "send success!\n");
    }

    close(newSock);
    close(s);

    // return 0;
}

void client_test()
{
    int s;
    int ret1 = -2;
    int ret2 = -2;

    int newSock;
    struct sockaddr_in addr;
    int addrLen;

    char sendbuf[] = "This is a message from 192.168.1.88";
    int  buf_len1 = sizeof(sendbuf);
    struct sockaddr_in srvr;

    /*
     *  创建 socket TCP
     */
    s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == -1) {
        printf("child socket error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "child socket error!\n");
        // return (GJB_PRI_FAIL());

    } else {
        printf("child socket success!\n");
        // GJB_PRT_INFO( "child socket success!\n");
    }

    memset(&srvr, 0, sizeof(srvr));

    srvr.sin_family = AF_INET;
    srvr.sin_port = htons(8080);
    srvr.sin_addr.s_addr = inet_addr(IP_ADDR);

    /*
     *  绑定 IP 地址 IP_ADDR 端口 8080
     */
    ret2 = bind(s, (struct sockaddr *)&srvr, sizeof(srvr));
    if (ret2 == -1) {
        printf("child bind error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "child bind error = %d!\n", errno);
        // return (GJB_PRI_FAIL());

    } else {
        printf("child bind success!\n");
        // GJB_PRT_INFO( "child bind success!\n");
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8081);
    addr.sin_addr.s_addr = inet_addr(IP_ADDR);
    addrLen = sizeof (addr);

    /*
     *  这里加个延时保证服务器程序已经 accept.
     */
    sleep(1);

    /*
     *  连接 IP_ADDR 端口 8081
     */
    newSock = connect(s,  (struct sockaddr *)&addr, addrLen);
    if (newSock == -1) {
        printf("child connect error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "child connect error!\n");
        // return (GJB_PRI_FAIL());

    } else {
        printf("child connect success!\n");
        // GJB_PRT_INFO( "child connect success!\n");
    }

    /*
     *  发送数据
     */
    ret1 = send(s, sendbuf, buf_len1, 0);
    if(ret1 == -1) {
        printf("child send error!\n");
        // return -1;
        // GJB_PRT_ERROR_INFO( "child send error!\n");
        // return (GJB_PRI_FAIL());
    } else {
        printf("child send success!\n");
        // GJB_PRT_INFO( "child send success!\n");
    }

    close(newSock);
    close(s);
    // return 0;
}

int main()
{
    pthread_t    tid, tid1;
    int          ret;
    void        *retval1;
    void        *retval2;

    ret = pthread_create(&tid, NULL, (void *)server_test, NULL);
    if (ret != 0) {
        printf("server_test create failed!\n");
        return -1;
        // GJB_PRT_ERROR_INFO( "server_test create failed!\n");
        // return (GJB_PRI_FAIL());
    }

    ret = pthread_create(&tid1, NULL, (void *)client_test, NULL);
    if (ret != 0) {
        printf("client_test create failed!\n");
        return -1;
        // GJB_PRT_ERROR_INFO( "client_test create failed!\n");
        // return (GJB_PRI_FAIL());
    }

    ret = pthread_join(tid, &retval1);
    if (ret != 0) {
        printf("pthread join server_test failed!\n");
        return -1;
        // GJB_PRT_ERROR_INFO( "pthread join server_test failed!\n");
        // return (GJB_PRI_FAIL());
    }

    ret = pthread_join(tid1, &retval2);
    if (ret != 0) {
        printf("pthread join client_test failed!\n");
        return -1;
        // GJB_PRT_ERROR_INFO( "pthread join client_test failed!\n");
        // return (GJB_PRI_FAIL());
    }

    if (((long)retval1 == 0) && ((long)retval2 == 0)) {
        return 0;
        // return (GJB_PRI_PASS());
    } else {
        return -1;
        // return (GJB_PRI_FAIL());
    }
}
